package com.polonium.pageobject.selenium;

import com.polonium.pageobject.annotations.Navigator;
import com.polonium.pageobject.annotations.WebPage;
import com.polonium.pageobject.utils.WebNavigator;
import org.fest.reflect.core.Reflection;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class SeleniumWebNavigator implements WebNavigator {
	private static final String SET_HIDDEN_SCRIPT = "document.getElementsByName('%s').item(0).value = '%s';";

	private final WebDriver webDriver;
	private final WebAppConfig webAppConfig;

	public SeleniumWebNavigator(WebDriver webDriver, WebAppConfig webAppConfig) {
		this.webDriver = webDriver;
		this.webAppConfig = webAppConfig;
	}

	@Override
	public <T> T createPage(Class<T> clazz) {
		T page = instantiatePage(webDriver, clazz);

		PageFactory.initElements(new AjaxElementLocatorFactory(webDriver, 2), page);

		for (Field field : clazz.getDeclaredFields()) {
			Navigator navigator = field.getAnnotation(Navigator.class);
			if (navigator != null) {
				Reflection.field(field.getName()).ofType(WebNavigator.class).in(page).set(this);
			}
		}
		return page;
	}

	@Override
	public <T> T goTo(Class<T> pageClass) {

		WebPage annotation = pageClass.getAnnotation(WebPage.class);

		String url = webAppConfig.getBaseUrl() + annotation.value();
		webDriver.get(url);
		return createPage(pageClass);
	}

	@Override
	public void setHiddenValue(String inputName, String value) {
		((JavascriptExecutor) webDriver).executeScript(String.format(SET_HIDDEN_SCRIPT, inputName, value));
	}

	private static <T> T instantiatePage(WebDriver driver, Class<T> pageClassToProxy) {
		try {
			try {
				Constructor<T> constructor = pageClassToProxy.getConstructor(WebDriver.class);
				return constructor.newInstance(driver);
			} catch (NoSuchMethodException e) {
				return pageClassToProxy.newInstance();
			}
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}
}
